#include "ChessGame.hpp"
#include <sstream>
#include <algorithm>
#include "PositionPair.hpp"
#include "MainWin.h"
#include "View.hpp"
#include <cassert>
#include "LinkManager.hpp"

ChessGame*	ChessGame::instance = 0;

ChessGame*	ChessGame::getInstance()
{
	if (ChessGame::instance == 0)
		ChessGame::instance = new ChessGame();
		
	return (ChessGame::instance);
}

ChessGame::ChessGame()
{}


void ChessGame::init()
{
	Chess::getInstance()->init();	
	view = new View();
	view->init();
	view->getStatusBox()->addMsg("Initializing...");
	view->getStatusBox()->addMsg("New game...");	
	scanStateUpdate();
}

void ChessGame::reset()
{
	try
	{
		Chess::getInstance()->init();
		view->getStatusBox()->addMsg("New game...");
		scanStateUpdate();
	}
	catch(Exception* e)
	{	throw e;	}
}

void ChessGame::save(string filename)
{
	try
	{
		
		string		main_str("Saving to file ");
		
		main_str += filename;
		main_str += string("...");
		view->getStatusBox()->addMsg(main_str.c_str());
		Chess::getInstance()->save(filename);
	}
	catch(Exception* e)
	{	throw e;	}
}

void ChessGame::load(string filename)
{
	try
	{
		Chess::getInstance()->load(filename);
		scanStateUpdate();
	}
	catch(Exception* e)
	{	throw e;	}
}

void ChessGame::waitingForAction()
{
	try
	{
		list<Action*>::iterator 	it;
		Action*						action = 0; 
		MoveAction*					maction = 0;
		CastlingAction*				caction = 0;
		
		mactions.clear();
		cactions.clear();
		
		for (it = state.cgs_lmoves.begin() ; it != state.cgs_lmoves.end() ; it++)
		{
			action = (*it);
			
			switch (action->getType())
			{
				case Action::MOVE:
					assert((maction = static_cast<MoveAction*>(action)) != 0);
					mactions[PositionPair(maction->getPos1(), maction->getPos2()).getHashKey()] = maction;
					maction->displayDbg();
					break;
					
				case Action::CASTLING:
					assert((caction = static_cast<CastlingAction*>(action)) != 0);
					cactions[caction->getCastlingType()] = caction;
					break;
				
				case Action::PAWN_PROMOTION:
					throw new CriticalException("ChessGame::waitingForAction : action is a PAWN_PROMOTION type !!");
					break;
			}
		}
		
		// updating signals/slots
		LinkManager::getInstance()->update(mactions, cactions, state.cgs_player);
	}
	catch(Exception* e)
	{	throw e;	}
}

void ChessGame::waitingForPawnPromotion()
{
	try
	{
		ppawn = state.cgs_ppawn;
		doAction(new PawnPromotionAction(ppawn, view->askForPawnPromotion()));
	}
	catch(Exception* e)
	{	throw e;	}
}

void ChessGame::gameOver()
{
	try
	{
		string						player_str((state.cgs_player == Chess::WHITE) ? "White" : "Black");
		string						main_str("Game Over : ");
		
		main_str += player_str; 
		
		// make modifications on the view to stop the game
		switch (state.cgs_dstate)
		{
			case Chess::NONE:
				break;
				
			case Chess::STALEMATE:
				view->getStatusBox()->addMsg("Game Over : Draw by stalemate !");
				break;
				
			case Chess::CHECKMATE_IMPOSSIBLE:
				view->getStatusBox()->addMsg("Game Over : Checkmate is impossible !");
				break;
		}
		
		switch (state.cgs_pstate)
		{
			case Chess::STANDARD:
				break;
				
			case Chess::CHECK:		
				break;
				
			case Chess::CHECKMATE:
				main_str += string(" is in checkmate !");
				view->getStatusBox()->addMsg(main_str.c_str());
				break;
		}
		
	}
	catch(Exception* e)
	{	throw e;	}
}


void	ChessGame::scanStateUpdate()
{	
	try
	{
		string						player_str;
		string						main_str("Turn ");
		stringstream				stream;
		stringstream				stream2;
		
		// clearing temporary values
		mactions.clear();
		cactions.clear();
		ppawn = 0;	
		// getting the state
		state = Chess::getInstance()->getState();
		
		player_str = string((state.cgs_player == Chess::WHITE) ? "White" : "Black");
		
		// updating string to send to status box
		stream2 << state.cgs_turn;
		main_str += stream2.str();
		main_str += string(" - ");
		main_str += player_str;

		
		if (state.cgs_pstate == Chess::CHECK)
			main_str += string(" [In CHECK] ");
		
		main_str += string(" : ");
		stream << state.cgs_lmoves.size();
		main_str += stream.str();
		main_str += string(" move");
		if (state.cgs_lmoves.size() > 1)
			main_str += string("s");
		main_str += string("...");
		
		// updating the view
		view->refresh();
		
		// getting input & launching signal/slots
		switch(state.cgs_estate)
		{
			case Chess::WAITING_FOR_ACTION:
				view->getStatusBox()->addMsg(main_str.c_str());
				this->waitingForAction();
				break;
				
			case Chess::WAITING_FOR_PAWN_PROMOTION:
				view->getStatusBox()->addMsg(main_str.c_str());
				this->waitingForPawnPromotion();
				break;
				
			case Chess::GAME_OVER:
				this->gameOver();
				break;
		}
	}
	catch(Exception* e)
	{	e->act();	}
}

void ChessGame::doAction(Action* action)
{
	try
	{			
		// giving action
		switch(Chess::getInstance()->execute(action))
		{
			case Chess::ILLEGAL_MOVE:
				break;
					
			case Chess::PAWN_PROMOTION_REQUIRED:
				break;
					
			case Chess::ILLEGAL_PAWN_PROMOTION:
				break;
					
			case Chess::GAME_IS_OVER:
				break;
				
			case Chess::NO_ERROR:
				break;
		}
		
		scanStateUpdate();
	}
	catch(Exception* e)
	{	throw e;	}
}

Chess::Color ChessGame::getCurrentPlayer() const
{	return state.cgs_player;	}

void ChessGame::doMoveAction(unsigned short hash_key)
{
	try
	{
		doAction(mactions[hash_key]);
	}
	catch(Exception* e)
	{	throw e;	}
}

View* ChessGame::getView() const
{	return (view);	}

ChessGame::~ChessGame()
{
	mactions.clear();
	cactions.clear();
	
	delete LinkManager::getInstance();
	delete view;
	delete Chess::getInstance();
}
